﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.WebPages;
using umbraco.MacroEngines;
using Eksponent.RazorRocks.Helpers;
using System.Web;

namespace Eksponent.RazorRocks.Umbraco
{
   
    public class RazorRocks<TItem> where TItem : class
    {
        private static string _keyPrefix;
        static RazorRocks()
        {
            _keyPrefix = "_RazorRocks_" + typeof(TItem).Name;
        }


        private static Func<TItem, string> _aliasSelector;
        private static Func<TItem> _currentItemResolver;

        public static void Initialize(Func<TItem, string> aliasSelector, Func<TItem> currentItemResolver)
        {            
            _aliasSelector = aliasSelector;
            _currentItemResolver = currentItemResolver;
        }


        private static readonly TemplateMatcher<TItem> TemplateMatcherInstance =
            new TemplateMatcher<TItem>(x => _aliasSelector(x));


        /// <summary>
        /// Gets the current node being displayed.
        /// </summary>
        public static TItem CurrentNode
        {
            get
            {
                var key = _keyPrefix + ".Current";
                var current = HttpContext.Current.Items[key] as TItem;
                if (current == null)
                {                    
                    HttpContext.Current.Items[key] = (current = _currentItemResolver());
                }
                return current;
            }
        }

        /// <summary>
        /// Gets the node passed to templates.
        /// </summary>
        public static TItem TemplateNode
        {
            get { return CurrentContext.Item; }
        }


        public static TemplateRenderContext<TItem> CurrentContext
        {
            get { return TemplateMatcherInstance.CurrentContext; }
        }


        /// <summary>
        /// If you use the "Wrap" syntax for your templates this is where you set return values
        /// </summary>
        public static dynamic ReturnValues
        {
            get { return WebPageContext.Current.PageData[RazorTemplateAction<TItem>.ReturnValueKey]; }
        }

        /// <summary>
        /// Forwards the called to the specified razor template. The node and other parameters are passed to the template, optionally with extraParameters added.
        /// </summary>
        /// <param name="razorTemplatePath">The razor template path.</param>
        /// <param name="extraParameters">The extra parameters.</param>        
        public static HelperResult Forward(string razorTemplatePath, object extraParameters = null)
        {
            if (!razorTemplatePath.EndsWith(".cshtml"))
            {
                razorTemplatePath += ".cshtml";
            }

            var parameters = WebPageContext.Current.PageData[RazorTemplateAction<TItem>.CallParametersKey];
            foreach (var kv in extraParameters.AsDictionary())
            {
                parameters[kv.Key] = kv.Value;
            }


            return WebPageContext.Current.Page.RenderPage(razorTemplatePath, parameters);
        }


        /// <summary>
        /// Renders the specified node.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="data">The subfolders (if any) as strings. If the last item is an anonymous type or dictionary the parameters will be passed to the template</param>
        /// <returns></returns>
        public static TemplateHelperResult Render(TItem node, params object[] data)
        {
            IDictionary<string, object> parameters = new Dictionary<string, object>();
            if (!data.IsNullOrEmpty())
            {
                var last = data[data.Length - 1];
                if (!(last is string))
                {
                    parameters = last.AsDictionary();
                    Array.Resize(ref data, data.Length - 1);
                }
            }
            return TemplateMatcherInstance.Render(node, parameters, data.Cast<string>().ToArray());
        }

        /// <summary>
        /// Renders the specified node with the template action specified (i.e. regardsless of configuration rules)
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="action">The action.</param>
        /// <param name="args">The args.</param>
        /// <returns></returns>
        public static TemplateHelperResult RenderWith(TItem node, TemplateAction<TItem> action, object args = null)
        {
            return TemplateMatcherInstance.RenderWith(node, action, args.AsDictionary());
        }

        private static volatile object _configureLock = new object();
        private static volatile bool _configured = false;
        /// <summary>
        /// Configures rules for templates.
        /// The actions are only executed once (i.e. subsequent calls will be ignored), hence the method can (and should) be called from a razor template.
        /// </summary>
        /// <param name="actions">The actions to perform on the configuration.</param>
        /// <param name="aliasSelector">If specified, a function that gets the node type alias from a TItem.</param>
        /// <param name="converter">A function that converters a DynamicNode to TItem.</param>
        /// <param name="reload">DO NOT EVER SET THIS TO TRUE IN A PRODUCTION ENVIRONMENT. if set to <c>true</c> the configuration is reloaded on every call.</param>

        public static void ConfigureTemplates(Action<TemplateMatcher<TItem>.ConfigSetter> actions,
            bool reload = false,
            Func<TItem> converter = null,
            Func<TItem, string> aliasSelector = null)
        {
            if (reload)
            {
                _configured = false;
            }

            if (!_configured)
            {
                lock (_configureLock)
                {
                    if (!_configured)
                    { 
                                           
                        if (aliasSelector != null)
                            _aliasSelector = aliasSelector;

                        if (converter != null)
                            _currentItemResolver = converter;

                        actions(TemplateMatcherInstance.Configure());
                    }
                }
                _configured = true;
            }
        }
    }
}
